package com.summerbird.mallchat.common.common.aspect;

import com.summerbird.mallchat.common.common.annotation.DoubleCache;
import com.summerbird.mallchat.common.common.domain.enums.CacheType;
import com.summerbird.mallchat.common.common.utils.RedisUtils;
import com.summerbird.mallchat.common.common.utils.SpElUtils;
import com.summerbird.mallchat.common.user.service.impl.PushService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.github.benmanes.caffeine.cache.Cache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Objects;

@Component
@Aspect
@Slf4j
public class DoubleCacheAspect {
    @Resource(name = "cache")
    private Cache cache;

    @Autowired
    private PushService pushService;

    @Pointcut("@annotation(com.summerbird.mallchat.common.common.annotation.DoubleCache)")
    public void cacheAspect() {
    }

    @Around("cacheAspect()")
    public Object around(ProceedingJoinPoint joinpoint) throws Throwable {
        // 1. 反射获取方法
        MethodSignature signature = (MethodSignature) joinpoint.getSignature();
        Method method = signature.getMethod();
        // 2. 解析并获取注解中的SpEL表达式的值
        DoubleCache doubleCache = method.getAnnotation(DoubleCache.class);
        String cacheName = SpElUtils.parseSpEl(method, joinpoint.getArgs(), doubleCache.cacheName());
        String key = SpElUtils.parseSpEl(method, joinpoint.getArgs(), doubleCache.key());
        // 3. 判断Cache类型：FULL、PUT、DELETE
        CacheType type = doubleCache.type();
        // 3.1 强制更新
        if(type.equals(CacheType.PUT)){
            Object result = joinpoint.proceed();
            cache.put(cacheName, result);
            RedisUtils.set(key, result, doubleCache.expiredTime(), doubleCache.unit());
            return result;
        }
        // 3.2 删除缓存
        else if(type.equals(CacheType.DELETE)){
            log.info("delete data");
            cache.invalidate(cacheName);
            RedisUtils.del(key);
            return joinpoint.proceed();
        }
        // 4. 依次从Caffeine、Redis中查找
        // 4.1 查找Caffeine中的数据
        Object result = cache.getIfPresent(cacheName);
        if(Objects.nonNull(result)){
            log.info("get data from caffeine");
            return result;
        }
        // 5. 从Redis中查询，并更新Caffeine中的值
        result = RedisUtils.get(key, Object.class);
        if(Objects.nonNull(result)){
            log.info("get data from redis");
            // 更新Caffeine的值
            cache.put(cacheName, result);
            return result;
        }
        // 6. 都没有，则执行proceed查询数据库，并将值设置进Redis和Cache中
        log.info("get data from database");
        result = joinpoint.proceed();
        if(Objects.nonNull(result)){
            log.info("Caffeine、Redis: update data from database");
            RedisUtils.set(key, result, doubleCache.expiredTime(), doubleCache.unit());
            cache.put(cacheName, result);
        }
        return result;
    }
}
